#pragma once

#include <functional>
#include <memory>
#include <string>

using namespace std;

#include "tnet.h"
#include "address.h"
#include "tcpserver.h"
#include "tcpconnection.h"

namespace tnet
{
    class TcpServer;
    class Address;
    class Connection;

    template<typename TConnection>
    class TcpService : public nocopyable
    {
    public:
        typedef std::shared_ptr<TConnection> DerivedConnPtr_t;
        typedef std::function<void (const DerivedConnPtr_t&, ConnEvent, const string&)> TcpCallback_t;
    
        TcpService(TcpServer* server);
        TcpService(TcpServer* server, const TcpCallback_t& callback,uint16_t length = 0);
        ~TcpService();

        void setCallback(const TcpCallback_t& callback);
        void setLength(uint16_t len);
        void setReuseport(bool on);
        void setAffinity(bool on);
        void setMaxTimeout(uint16_t maxTimeout);

        int  start(const Address& addr,size_t maxProcess = 1);

        static void sendResponse(const DerivedConnPtr_t& conn,const string& response);
    private:
        void onConnEvent(const ConnectionPtr_t&, ConnEvent, const void* context);
        void onRequest(const DerivedConnPtr_t& conn, ConnEvent event, const string& request_data);
        static void dummyTcpCallback(const DerivedConnPtr_t& conn,ConnEvent event, const string& context);
    private:
        TcpServer* m_server;
        TcpCallback_t  m_callback;
        uint16_t m_length;
        bool m_reuseport;
        bool m_affinity;
        uint16_t m_maxTimeout;
    };   

    template<typename TConnection>
    void TcpService<TConnection>::dummyTcpCallback(const DerivedConnPtr_t& conn,ConnEvent event, const string& context)
    {
    }

    template<typename TConnection>
    TcpService<TConnection>::TcpService(TcpServer* server)
        : m_server(server)
        , m_callback(std::bind(&TcpService<TConnection>::dummyTcpCallback,_1,_2,_3))
        , m_length(0)
        , m_reuseport(true)
        , m_affinity(true)
        , m_maxTimeout(120)
    {
    }

    template<typename TConnection>
    TcpService<TConnection>::TcpService(TcpServer* server,const TcpCallback_t& callback,uint16_t length)
        : m_server(server)
        , m_callback(callback)
        , m_length(length)
        , m_reuseport(true)
        , m_affinity(true)
        , m_maxTimeout(120)
    {
    }    
    
    template<typename TConnection>
    TcpService<TConnection>::~TcpService()
    {
    }

    template<typename TConnection>
    void TcpService<TConnection>::setCallback(const TcpCallback_t& callback)
    {
        m_callback= callback;    
    }   

    template<typename TConnection>
    void TcpService<TConnection>::setLength(uint16_t len)
    {
        m_length = len;
    }  

    template<typename TConnection>
    void TcpService<TConnection>::setReuseport(bool on)
    {
        m_reuseport = on;
    }

    template<typename TConnection>
    void TcpService<TConnection>::setAffinity(bool on)
    {
        m_affinity = on;
    }

    template<typename TConnection>
    void TcpService<TConnection>::setMaxTimeout(uint16_t maxTimeout)
    {
        m_maxTimeout = maxTimeout;
    }      

    template<typename TConnection>
    int TcpService<TConnection>::start(const Address& addr,size_t maxProcess)
    {
		m_server->setReuseport(m_reuseport);	
		m_server->setCpuaffinity(m_affinity);    
        m_server->setMaxIdleTimeout(m_maxTimeout);    
        m_server->start(
            addr,
            std::bind(&TcpService<TConnection>::onConnEvent, this,_1, _2, _3),
            maxProcess); 
        return 0;
    }

    template<typename TConnection>
    void TcpService<TConnection>::onConnEvent(const ConnectionPtr_t& conn, ConnEvent event, const void* context)
    {
        switch(event)
        {
            case Conn_EstablishedEvent:
                {
                    DerivedConnPtr_t tcpConn = std::make_shared<TConnection>(conn, 
                        std::bind(&TcpService<TConnection>::onRequest, this, _1, _2, _3),m_length);
            
                    conn->setEventCallback(std::bind(&TConnection::onConnEvent, tcpConn, _1, _2, _3));
                    m_callback(tcpConn,event,string());
                }
                break;
            default:
                LOG_INFO("error when enter this");
                return;
        }
    }

    template<typename TConnection>
    void TcpService<TConnection>::onRequest(const DerivedConnPtr_t& tcpConn, ConnEvent event, const string& context)
    {
        m_callback(tcpConn,event,context);
    }   

    template<typename TConnection>
    void  TcpService<TConnection>::sendResponse(const DerivedConnPtr_t& tcpConn,const string& response)
    {
        ConnectionPtr_t c = tcpConn->lockConn();
        if(!c)
        {
            return;    
        }    
        if(response.empty())
        {
            c->setEnableWriteEvent(true);
            return;
        }
        c->send(response);      
        return;
    }    

}